1

使用vue 这么长时间,对vue 对源码了解还是不多,作为一个不甘平庸的前端小白,决定奋起,那么今天我们就来谈谈vue 的render 函数;
打开源码,我发现render 函数返回一个VNode; 可是我们并未在模版中写render 呀,这又是一个什么样的过程呢?
当我们创建一个Vue 实例的时候,我们会把template 编译生成render 函数;render 函数返回一个VNode; vue/src/core/instance/render.js

// 返回一个VNode;方法是实例的一个私有方法,它用来把实例渲染成一个虚拟 Node。
  Vue.prototype._render = function (): VNode {
    const vm: Component = this
    //拿到render 函数,可以是用户自己写,也可以通过编译生成;
    const { render, _parentVnode } = vm.$options

    if (_parentVnode) {
      vm.$scopedSlots = normalizeScopedSlots(
        _parentVnode.data.scopedSlots,
        vm.$slots,
        vm.$scopedSlots
      )
    }

    // set parent vnode. this allows render functions to have access
    // to the data on the placeholder node.
    vm.$vnode = _parentVnode
    // render self
    let vnode
    try {
      // There's no need to maintain a stack becaues all render fns are called
      // separately from one another. Nested component's render fns are called
      // when parent component is patched.
      //不需要维护堆栈,因为所有render 函数都是单独调用的。当修补父组件时,将调用嵌套组件的呈现FN
      currentRenderingInstance = vm
      // 第一个参数是当前上下文,_renderProxy 是在initProxy 中定义的,第二个是
      //vm.renderProxy 在生产环境下就是vm,在开发环境可能是一个proxy 对象;vm.$createElement 在initRender 函数中有定义;
      vnode = render.call(vm._renderProxy, vm.$createElement)
    } catch (e) {
      handleError(e, vm, `render`)
      // return error render result,
      // or previous vnode to prevent render error causing blank component
      /* istanbul ignore else */
      if (process.env.NODE_ENV !== 'production' && vm.$options.renderError) {
        try {
          vnode = vm.$options.renderError.call(vm._renderProxy, vm.$createElement, e)
        } catch (e) {
          handleError(e, vm, `renderError`)
          vnode = vm._vnode
        }
      } else {
        vnode = vm._vnode
      }
    } finally {
      currentRenderingInstance = null
    }
    // if the returned array contains only a single node, allow it
    if (Array.isArray(vnode) && vnode.length === 1) {
      vnode = vnode[0]
    }
    // return empty vnode in case the render function errored out
    // 如果渲染函数出错,则返回一个空的VNode;

    // 如果vnode 不是VNode 的一个实例;
    if (!(vnode instanceof VNode)) {
      // vnode 是一个array 说为vnode 是多个根节点;VNode 虚拟DOM;
      if (process.env.NODE_ENV !== 'production' && Array.isArray(vnode)) {
        // 从render 函数中返回多个根节点,应该返回单个的根节点;
        warn(
          'Multiple root nodes returned from render function. Render function ' +
          'should return a single root node.',
          vm
        )
      }
      vnode = createEmptyVNode()
    }
    // set parent
    vnode.parent = _parentVnode
    return vnode
  }

那么我们手写render,跟使用编译的有何区别?
编译生成render 函数
index.html'

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue-analysis</title>
  </head>
  <body>
    <div id="app">
      {{message}} 
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

main.js'

import Vue from 'vue'

/* eslint-disable no-new */
new Vue({
  el: '#app',
  data() {
    return {
      message: 'hello vue'
    }
    
  }
  
})

手写render 函数
index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>vue-analysis</title>
  </head>
  <body>
    <div id="app">
    </div>
    <!-- built files will be auto injected -->
  </body>
</html>

main.js

import Vue from 'vue'

/* eslint-disable no-new */
new Vue({
  el: '#app',
  render(createElememt) {
    return createElememt('div',{
      attrs: {
        id: "#app1"
      }
    }, this.message)
  },
  data() {
    return {
      message: 'hello vue'
    }
    
  }
  
})

总结:
// 与直接在html中写不一样,她没有从插值变换过来的过程,之前我们是在html 中定义了插值,她在不执行的时候,先把html 的空文件渲染出来,然后在new Vue 之后,执行mounted 方法再把插值从message 中替换成真实的数据,

// 我们通过render 函数,直接手写render,我们不用在页面上显示那个插值,而是通过render函数,当它执行完毕以后,会把我们的message 替换上去,这样体验会更好一些;这里我们手写了render函数,我们就没有把template 转换成render函数这一步了;

//注意,我们挂载的元素会替换掉我们的根节点,这也是我们为什么不能用body 作为挂载点的原因,会把body 替换掉;

职场小白south Joe,望各位大神批评指正,祝大家学习愉快!


南乔
15 声望3 粉丝

我爱敲代码,代码给我快乐